Nun schauen wir uns Objekte in JavaScript an und vergleichen die Konzepte mit Java. JavaScript ist in erster Linie objektbasierend. Funktionen sind in JavaScript auch Objekte. Objekte können von Objekten erben. Dies nennt man prototypische Vererbung. In Java legt die Klasse die Struktur der erzeugten Objekte fest. Alle erzeugten Objekte haben in Java denselben Aufbau. In JavaScript legt eine Konstruktorfunktion die Struktur der erzeugten Objekte fest. Erzeugte Objekte können in JavaScript einen unterschiedlichen Aufbau haben. Objekte können erzeugt werden über ein Objekt-Literal via JSON. Oder über eine Konstruktor-Funktion, die mit "new" aufgerufen wird. Oder über eine Factory-Funktion, die ohne "new" aufgerufen wird. Als Konvention fangen die Konstruktor-Funktionen mit Großbuchstaben an und die Factory-Funktionen fangen mit Kleinbuchstaben an. Hier haben wir eine Erzeugung via JSON Objekt-Literal. Was JSON genau ist, schauen wir uns in einem anderen Video genauer an. Offensichtlich ist es eine einfache Syntax mit einer Art von Key-Value-Paaren. Als Resultat gibt es eine Referenz auf das Objekt mit Namen Herbert Schulze. Bedenken Sie, dass dafür keine separate Klasse existiert! Sie bekommen sofort das Objekt. In diesem Beispiel definieren wir eine Konstruktor-Funktion mit zwei Eigenschaften und einer Methode. Danach rufen wir diese Konstruktor-Funktion zweimal mit verschiedenen Werten auf und erhalten so zwei verschiedene Objekt-Referenzen, nämlich den Meier und den Müller. Und dies ist die Erzeugung von zwei neuen JavaScript-Objekten mittels einer Factory-Funktion. Das Objekt wird hier über einer Referenz innerhalb der Funktion verwaltet. Und auch hier kommt wieder ein Meier und ein Müller heraus. Man kann die Factory-Funktion und die Erzeugung via JSON auch mischen. Bei dem Aufruf beziehungsweise bei der Verwendung der Funktion ist kein Unterschied zu bemerken. Sowohl in Java, als auch in JavaScript kann man Vererbung verwenden. Hier sehen wir ein Beispiel in Java. Ein Manager ist ein spezieller Mitarbeiter. Der Manager erbt also vom Mitarbeiter und ergänzt diesen um weitere Eigenschaften und/oder Methoden. Ein Konstruktor der Oberklasse kann in einem Konstruktor mit "super" aufgerufen werden. Hier hat jeder Mitarbeiter einen Namen und ein Alter. Abschließend sehen wir den Aufruf des Konstruktors in Java. In JavaScript sieht das einfach so aus. Es gibt ja keine Klassen. Der "call"-Funktionsaufruf auf die Funktion des Managers entspricht dem "super"-Aufruf im Konstruktor. Dadurch wird die Verbindung zwischen einem Manager und einem Mitarbeiter hergestellt. Der Aufruf des Konstruktors sieht dann fast identisch zu Java aus. Zu jeder Funktion gehört ein Prototyp, das sogenannte prototype-object. Der Zugriff erfolgt über Funktionsname-Punkt-Prototype. Wird die Funktion als Konstruktor mit new verwendet, dann hat das erzeugte Objekt eine Referenz zum Prototypen. Sind Attribute und Funktionen nicht im Objekt definiert, dann werden sie im Prototypen gesucht. Schauen wir uns ein Tier als Beispiel an. Jedes Tier soll erst einmal vier Beine und zwei Augen haben. Jedes Tier soll fressen und flüchten können. Dies wird in JavaScript in der Prototyp-Funktion hinterlegt. Spinnen sind Tiere, also haben wir hier eine Vererbung. Die Pfeile im Diagramm entsprechen leider nicht dem UML-Standard der Vererbung. Spinnen haben als spezielle Tiere aber acht Beine und zwölf Augen. Enten sind auch Tiere, die aber auf eine andere Art und Weise flüchten. Dies muss also neu definiert werden. Achja, Dackel sind auch Tiere. Sie haben aber nichts Besonderes. Hier haben wir die Definition des Prototyps in JavaScript. Jedes Tier soll wohl noch einen Namen haben. Diese Anforderung wurde in der Grafik nicht festgehalten. Statt ein Rufname ist hiermit aber eher eine Gattung oder Art gemeint. Danach kommen die Definitionen der Methoden beziehungsweise Funktionen, wie ein Tier frißt und flüchtet. Danach wird festgelegt, dass jedes Tier vier Beine und zwei Augen haben soll. Danach wird die Spinne definiert, die acht Beine und zwölf Augen haben soll. Sie kann fressen und flüchten wie jedes Tier. Abschließend werden die Anzahl der Beine und Augen ausgegeben. Probieren Sie es aus! Nun kommt die Ente, die anders flüchtet als ein normales Tier. Das wird hier definiert. Achja, die Anzahl der Beine stimmt bei einer Ente auch nicht mit einem Standard-Tier überein. Das wurde in der Grafik vergessen! Abschließend wieder der Test des Fressens und Flüchtens sowie die Ausgabe der Anzahl an Beinen und Augen. Und nun noch der Dackel als drittes und letztes spezielles Tier. Ein Dackel hat weder besondere Eigenschaften noch Fähigkeiten. Erkennen Sie, wie in JavaScript die Objektorientierung gehandhabt wird? In JavaScript können Objekte als Vorlage für neue Objekte dienen. Dies nennt man prototypische Vererbung. Mit "object Punkt create object" kann man ein Objekt erzeugen, das das gegebene Objekt als Vorlage benutzt. Das erzeugte Objekt kann man anpassen: erweitern, Methoden verändern et cetera. Von dem neuen Objekt kann man wieder weitere Objekte erzeugen. Beginnen wir mit der prototypischen Vererbung bei dem Tier-Prototyp. Das Tier soll als Eigenschaft einen Namen haben und drei Methoden. Hier sehen Sie, wie Sie das konkrete Objekt "Felizitas" aus dem Prototyp heraus anlegen. Das Tier ist eine Katze, die das Bewegen neu definiert mit "leise schleichen". Eine Perserkatze ist eine spezielle Katze. Die Referenz auf das vorhandene Katzenobjekt kann nun benutzt werden, um davon weiter abzuleiten. Diese Perserkatze soll anders fressen. Ähnlich wie bei Java kann man mittels des Operators "instance-of" überprüfen, ob die Konstruktor-Prototyp-Eigenschaft in der Prototypkette des überprüften Objektes vorkommt. Dazu sehen Sie einige Beispiel-Abfragen aus der aktuellen Vererbungskette aus unserem Beispiel. Bitte programmieren Sie das nach und vollziehen Sie die Ausgaben nach. "True" als Wahrheitswert und auch „Hallo“ als String-Primitiv sind in JavaScript übrigens primitive Typen! Es ist zur Laufzeit sogar möglich, Eigenschaften und Methoden von Objekten mit "delete" komplett zu entfernen. Hier sehen Sie das am Beispiel vom Hund "Hasso", der nachher kein Alter mehr hat und auch nicht mehr bellen kann. Im Gegensatz zu Java kennt JavaScript zunächst keine Zugriffsbeschränkungen. Alle Attribute eines Objektes sind immer erreichbar und änderbar. Es gibt also erst einmal keine Kapselung und kein Information-Hiding. Information-Hiding kann trotzdem erreicht werden mit folgendem Ablauf: Man verwendet zunächst eine Factory-Methode. Die Methode erzeugt ein Objekt für die privaten Variablen. Die Methode erzeugt ein Objekt für die öffentlichen Funktionen. Dem privaten Objekt werden alle privaten Variablen und privaten Funktionen hinzugefügt. Das öffentliche Objekt greift auf die privaten Daten zu, was es wegen der Sichtbarkeit der Daten innerhalb der Funktion darf. Das öffentliche Objekt wird abschließend zurückgegeben. Hier sehen Sie den Ablauf aus der vorherigen Folie an einem JavaScript-Beispiel. Ein Tier soll als Eigenschaft einen Namen haben. Dieser Name soll genauso gekapselt sein wie in Java und nur über Getter und Setter zugreifbar sein. Dazu brauchen Sie intern das zunächst leere Objekt "that" für die Methoden und das zunächst leere Objekt "my" für die Eigenschaften. Nach der Zuweisung des Namens werden dann die Getter und Setter programmiert. Interessant ist die Rückgabe des "that"-Objektes, das seinerseits das my-Objekt nicht direkt kennt. Dadurch wird die Kapselung erreicht. In den letzten vier Zeilen sehen Sie dann den schon fast traditionellen objektorientierten Zugriff über die Getter und Setter, wobei die Eigenschaft "name" quasi "private" ist. Hier sehen Sie wie eine Katze als spezielles Tier erzeugt werden kann unter Einhaltung der Kapselung der Eigenschaften und Information-Hiding. Zusätzlich wird eine Funktion beziehungsweise Methode definiert, die typisch für Katzen ist: Nämlich das Schnurren. Schauen wir uns nun die Ausnahmebehandlung innerhalb von Funktionen an. Es existiert ein try, catch, finally ähnlich wie in Java. Ausnahmen sind jedoch leider nicht typisiert. Daher gibt es nur einen catch-Block pro try. Und finally wird (wie in Java) immer ausgeführt. Hier haben wir ein Beispiel für die Ausnahmebehandlung in JavaScript. Beim Werfen eines neuen Fehlers kann eine Fehlermeldung mitgegeben werden. Es gibt nur einen einzigen catch-Block. Und abschließend das optionale "finally". Gibt es abschließend nun doch Klassen? Denn seit ES2015 besitzt JavaScript eine class-Syntax. Diese ist jedoch nur Kosmetik, die Eigenschaften sind immer noch öffentlich lesend und schreibend zugreifbar, also ähnlich zu "public" in Java. Die strikte Datenkapselung als Grundprinzip der Objektorientierung ist also in JavaScript weiterhin nicht vorgesehen.